home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Plus Special 17
/
AMIGAplus Sonderheft 17 (1999)(ICP)(DE)[!].iso
/
PD
/
Anwendungen
/
-DataTypes-
/
mpegsystem
/
dispatch.c
< prev
next >
Wrap
C/C++ Source or Header
|
1998-06-17
|
79KB
|
2,375 lines
/*
**
** $VER: dispatch.c 1.5 (1.6.98)
** mpegsystem.datatype 1.5
**
** Dispatch routine for a DataTypes class
**
** Written 1997/1998 by Roland 'Gizzy' Mainz
** Original example source from David N. Junod
**
*/
/* main includes */
#include "classbase.h"
#include "classdata.h"
/*****************************************************************************/
/* is animation.datatype V41 ? */
#define ISV41 ((cb -> cb_SuperClassBase -> lib_Version) == 41U)
/*****************************************************************************/
/* local prototypes */
/* virtual demux fs-handler related */
DISPATCHERFLAGS static void Handler( void );
static void RunHandler( struct ClassBase *cb, struct MPEGSystemInstData *msid );
static void KillHandler( struct ClassBase *cb, struct MPEGSystemInstData *msid );
static void dispatch_packet( struct ClassBase *cb, struct MPEGSystemInstData *msid, struct DosPacket *dp );
static BPTR GetStreamLock( struct ClassBase *cb, struct MPEGSystemInstData *msid, ULONG num, BOOL isaudio );
static void FreeLock( struct ClassBase *cb, struct MPEGSystemInstData *msid, struct FileLock *fl );
static BOOL SendSeek( struct ClassBase *, struct MPEGSystemInstData *, struct FileHandle *, LONG, LONG );
static BOOL SendRead( struct ClassBase *, struct MPEGSystemInstData *, struct FileHandle *, UBYTE *, ULONG );
static struct DosPacket *ObtainDOSPacket( struct ClassBase *, struct MPEGSystemInstData * );
static void ReleaseDOSPacket( struct ClassBase *, struct MPEGSystemInstData *, struct DosPacket * );
static void STRPTR2BSTR( STRPTR s );
/* MPEG system stream demultiplexer related */
static UBYTE *get_buf_data( struct ClassBase *cb, struct MPEGSystemInstData *msid, long read_len );
static UBYTE *calc_time_stamp( struct ClassBase *cb, struct MPEGSystemInstData *msid, UBYTE *buf_ptr , long *time_stamp );
static UBYTE get_next_start_code( struct ClassBase *cb, struct MPEGSystemInstData *msid );
static void read_pack_header( struct ClassBase *cb, struct MPEGSystemInstData *msid, PACK_header *pki );
static void read_system_header( struct ClassBase *cb, struct MPEGSystemInstData *msid, SYSTEM_header *si );
static void read_packet_header( struct ClassBase *cb, struct MPEGSystemInstData *msid, PACKET_header *pi );
static void print_pack_header( struct ClassBase *cb, struct MPEGSystemInstData *msid, PACK_header *pki );
static void print_system_header( struct ClassBase *cb, struct MPEGSystemInstData *msid, SYSTEM_header *si );
static void print_packet_header( struct ClassBase *cb, struct MPEGSystemInstData *msid, PACKET_header *pi );
static void exitdemux( struct ClassBase *cb, struct MPEGSystemInstData *msid, LONG result, LONG result2 );
static void demux( struct ClassBase *cb, struct MPEGSystemInstData *msid, BPTR file );
/* class related */
static BOOL ScanFrames( struct ClassBase *, Object * );
static void mysprintf( struct ClassBase *, STRPTR, STRPTR, ... );
static ULONG SaveMPEGSystem( struct ClassBase *cb, struct IClass *cl, Object *o, struct dtWrite *dtw );
/*****************************************************************************/
/* Create "mpegsystem.datatype" BOOPSI class */
struct IClass *initClass( struct ClassBase *cb )
{
struct IClass *cl;
/* Create our class... */
if( cl = MakeClass( MPEGSYSTEMDTCLASS, ANIMATIONDTCLASS, NULL, (ULONG)sizeof( struct MPEGSystemInstData ), 0UL ) )
{
#define DTSTACKSIZE (16384UL)
cl -> cl_Dispatcher . h_Entry = (HOOKFUNC)StackSwapDispatch; /* see stackswap.c */
cl -> cl_Dispatcher . h_SubEntry = (HOOKFUNC)Dispatch; /* see stackswap.c */
cl -> cl_Dispatcher . h_Data = (APTR)DTSTACKSIZE; /* see stackswap.c */
cl -> cl_UserData = (ULONG)cb;
AddClass( cl );
}
return( cl );
}
/*****************************************************************************/
/* class dispatcher */
DISPATCHERFLAGS
ULONG Dispatch( REGA0 struct IClass *cl, REGA2 Object *o, REGA1 Msg msg )
{
struct ClassBase *cb = (struct ClassBase *)(cl -> cl_UserData);
struct MPEGSystemInstData *msid;
ULONG retval = 0UL;
switch( msg -> MethodID )
{
/****** mpegsystem.datatype/OM_NEW *******************************************
*
* NAME
* OM_NEW -- Create a mpegsystem.datatype object.
*
* FUNCTION
* The OM_NEW method is used to create an instance of the
* mpegsystem.datatype class. This method is passed to the superclass
* first. After this, mpegsystem.datatype parses the prefs file and
* makes a scan through the system stream to get index information
* about the single system stream packets.
* After all, the mpegsystem.datatype starts two objects which reads
* the single video and audio streams trougth an internal
* "demultiplexer" (system stream splitter) filesystem.
*
* Subclasses of mpegsystem.datatype are not supported. Any attempt to
* create a subclass object of mpegsystem.datatype will be rejected by
* this method.
*
* ATTRIBUTES
* The following attributes can be specified at creation time.
*
* DTA_SourceType (ULONG) -- Determinates the type of DTA_Handle
* attribute. Only DTST_FILE is supported.
* If any other type was set in a given DTA_SourceType,
* OM_NEW will be rejected.
* Defaults to DTST_FILE.
*
* DTA_Handle -- For DTST_FILE, a BPTR filehandle is expected. This
* handle will be created by datatypesclass depeding on the DTF_#?
* flag, which is DTF_BINARY here. DTST_FILE, datatypesclass
* creates a file handle from the given DTA_Name and DTA_Handle
* (a BPTR returned by Lock).
* A DTST_RAM (create empty object) source type requires a NULL
* handle.
*
* RESULT
* If the object was created a pointer to the object is returned,
* otherwise NULL is returned.
*
******************************************************************************
*
*/
case OM_NEW:
{
struct TagItem *ti;
/* We only support DTST_FILE or DTST_RAM as source type */
if( ti = FindTagItem( DTA_SourceType, (((struct opSet *)msg) -> ops_AttrList) ) )
{
if( ((ti -> ti_Data) != DTST_FILE)
#ifdef HAS_ENCODER
&& ((ti -> ti_Data) != DTST_RAM)
#endif /* HAS_ENCODER */
)
{
SetIoErr( ERROR_OBJECT_WRONG_TYPE );
break;
}
}
#ifndef HAS_ENCODER
/* This must not be a subclass of mpegsystem.datatype
* (not implemented yet)
*/
if( o == (Object *)cl )
#endif /* !HAS_ENCODER */
{
if( retval = DoSuperMethodA( cl, o, msg ) )
{
/* Load/scan frames... */
if( !ScanFrames( cb, (Object *)retval ) )
{
/* Something went fatally wrong, dispose object */
CoerceMethod( cl, (Object *)retval, OM_DISPOSE );
retval = 0UL;
}
}
}
#ifndef HAS_ENCODER
else
{
/* Subclasses of mpegsystem.datatype are not implemented */
SetIoErr( ERROR_NOT_IMPLEMENTED );
}
#endif /* !HAS_ENCODER */
}
break;
/****** mpegsystem.datatype/OM_DISPOSE ***************************************
*
* NAME
* OM_DISPOSE -- Delete a mpegsystem.datatype object.
*
* FUNCTION
* The OM_DISPOSE method is used to delete an instance of the
* mpegsystem.datatype class. This method is passed to the superclass
* when it has completed.
*
* RESULT
* The object is deleted. 0UL is returned.
*
******************************************************************************
*
*/
case OM_DISPOSE:
{
LONG saved_ioerr = IoErr(); /* Preserve I/O error */
/* Get a pointer to our object data */
msid = (struct MPEGSystemInstData *)INST_DATA( cl, o );
/* Wait for any outstanding blitter usage (which may use one of our bitmaps) */
WaitBlit();
/* Dispose the current audio and video objects */
DisposeDTObject( (msid -> msid_CurrVideo) );
DisposeDTObject( (msid -> msid_CurrAudio) );
/* Kill our virtual demux handler */
KillHandler( cb, msid );
/* Delete the frame pool */
DeletePool( (msid -> msid_Pool) );
/* Close verbose output file */
if( (msid -> msid_VerboseOutput) && ((msid -> msid_VerboseOutput) != -1L) )
{
Close( (msid -> msid_VerboseOutput) );
}
/* Dispose object */
DoSuperMethodA( cl, o, msg );
/* Restore I/O error */
SetIoErr( saved_ioerr );
}
break;
case OM_UPDATE:
{
if( DoMethod( o, ICM_CHECKLOOP ) )
{
break;
}
}
case OM_SET:
{
/* Pass the attributes to the animation class and force a refresh if we need it */
if( retval = DoSuperMethodA( cl, o, msg ) )
{
/* The following statement is commented out because mpegsystem.datatype does not allow
* subclasses when the executable was compiled without the HAS_ENCODER define. Thus, the
* following statement is NOP unless subclasses are supported...
*/
#ifdef HAS_ENCODER
/* Top instance ? */
if( OCLASS( o ) == cl )
#endif /* HAS_ENCODER */
{
struct RastPort *rp;
/* Get a pointer to the rastport */
if( rp = ObtainGIRPort( (((struct opSet *)msg) -> ops_GInfo) ) )
{
struct gpRender gpr;
/* Force a redraw */
gpr . MethodID = GM_RENDER;
gpr . gpr_GInfo = ((struct opSet *)msg) -> ops_GInfo;
gpr . gpr_RPort = rp;
gpr . gpr_Redraw = GREDRAW_UPDATE;
DoMethodA( o, (Msg)(&gpr) );
/* Release the temporary rastport */
ReleaseGIRPort( rp );
/* We did an update... */
retval = 0UL;
}
}
}
}
break;
/****** mpegsystem.datatype/DTM_WRITE *******************************************
*
* NAME
* DTM_WRITE -- Save data
*
* FUNCTION
* This method saves the object's contents to disk.
*
* If dtw_Mode is DTWM_IFF, the method is passed unchanged to the
* superclass, animation.datatype, which writes a single IFF ILBM
* picture.
*
* If dtw_mode is DTWM_RAW, the object saved an MPEG System stream to
* the filehandle given, starting with the current frame until
* the end is reached.
* The sequence saved can be controlled by the ADTA_Frame, ADTA_Frames
* and ADTA_FrameIncrement attributes (see TAGS section below).
*
* TAGS
* When writing the local ("raw") format, MPEG System movie, the
* following attributes are recognized:
*
* ADTA_Frame (ULONG) - start frame, saving starts here.
* Defaults to the current frame displayed.
*
* ADTA_Frames (ULONG) - the number of frames to be saved,
* Defaults to (max_num_of_frames - curr_frame).
*
* ADTA_FrameIncrement (ULONG) - frame increment when saving.
* Defaults to 1, which means: "jump to next frame".
*
* NOTE
* This function is not implemented yet. A possible implementation
* would implement ACTION_WRITE in the virtial implementation and uses
* mpegvideo.datatype and mpegaudio.datatype encoders...
*
* RESULT
* Returns 0 for failure (IoErr() returns result2), non-zero
* for success.
*
******************************************************************************
*
*/
case DTM_WRITE:
{
struct dtWrite *dtw;
dtw = (struct dtWrite *)msg;
/* Local data format (MPEG System) requested ? */
if( (dtw -> dtw_Mode) == DTWM_RAW )
{
retval = SaveMPEGSystem( cb, cl, o, dtw );
}
else
{
/* Pass msg to superclass (which writes a single frame as an IFF ILBM picture)... */
retval = DoSuperMethodA( cl, o, msg );
}
}
break;
/****** mpegsystem.datatype/ADTM_LOADFRAME ***********************************
*
* NAME
* ADTM_LOADFRAME -- Load frame
*
* FUNCTION
* The ADTM_LOADFRAME method is used to obtain the bitmap and timing
* data of the animation.
* The method is passed to the embedded mpegvideo.datatype object.
* If successfull, the sample data of struct adtFrame are replaced
* by the corresponsing audio data from the embedded
* mpegaudio.datatype object.
*
* RESULT
* Returns the result from the embedded mpegvideo.datatype object
* (including Result2).
*
******************************************************************************
*
*/
case ADTM_LOADFRAME:
{
struct adtFrame *alf = (struct adtFrame *)msg;
/* Get a pointer to our object data */
msid = (struct MPEGSystemInstData *)INST_DATA( cl, o );
if( msid -> msid_CurrVideo )
{
if( retval = DoMethodA( (msid -> msid_CurrVideo), msg ) )
{
/* Add here the audio part..
* A better way would be to send SDTM_LOADSAMPLE to load the sample
* fragment
*/
if( msid -> msid_Sample )
{
UBYTE *sample = msid -> msid_Sample;
ULONG sample_offset = (alf -> alf_Frame) * (msid -> msid_SamplesPerFrame);
ULONG len = ((alf -> alf_Duration) + 1UL) * (msid -> msid_SamplesPerFrame);
/* Sample completely out-of-range ? */
if( sample_offset >= (msid -> msid_SampleLength) )
{
/* No sample data ! */
sample = NULL;
sample_offset = 0UL;
len = 0UL;
}
else
{
/* Sample partial out of range ? */
if( (sample_offset + len) > (msid -> msid_SampleLength) )
{
/* Cut it ! */
len -= ((sample_offset + len) - (msid -> msid_SampleLength));
}
}
/* Fill in the message body */
alf -> alf_Sample = sample + sample_offset;
alf -> alf_SampleLength = len;
alf -> alf_Period = msid -> msid_Period;
}
}
}
else
{
/* No mpegvideo.datatype object (Should not occur, but...). */
SetIoErr( ERROR_OBJECT_NOT_FOUND );
}
}
break;
/****** mpegsystem.datatype/ADTM_UNLOADFRAME ************************************
*
* NAME
* ADTM_UNLOADFRAME -- Unload frame contents
*
* FUNCTION
* The ADTM_UNLOADFRAME method is used to release the contents of a
* animation frame.
*
* This method is passed to the embedded mpegvideo.datatype object.
*
* RESULT
* Returns the result from the embedded mpegvideo.datatype object.
*
******************************************************************************
*
*/
case ADTM_UNLOADFRAME:
{
/* Get a pointer to our object data */
msid = (struct MPEGSystemInstData *)INST_DATA( cl, o );
if( msid -> msid_CurrVideo )
{
retval = DoMethodA( (msid -> msid_CurrVideo), msg );
/* Here we should do an SDTM_UNLOADSAMPLE if the sample data were
* obtained using SDTM_LOADSAMPLE
*/
}
}
break;
/* Let the superclass handle everything else */
default:
{
retval = DoSuperMethodA( cl, o, msg );
}
break;
}
return( retval );
}
static
BOOL ScanFrames( struct ClassBase *cb, Object *o )
{
struct MPEGSystemInstData *msid = (struct MPEGSystemInstData *)INST_DATA( (cb -> cb_Lib . cl_Class), o );
BOOL success = FALSE;
LONG error = 0L;
InitSemaphore( (&(msid -> msid_SigSem)) );
/* Create a memory pool for frame nodes */
if( msid -> msid_Pool = CreatePool( MEMF_PUBLIC, 8192UL, 8192UL ) )
{
BPTR fh; /* handle (file handle) */
ULONG sourcetype; /* type of stream (either DTST_FILE or DTST_RAM) */
/* Prefs defaults */
/* Read prefs */
ReadENVPrefs( cb, msid );
/* Get file handle, handle type and BitMapHeader */
if( GetDTAttrs( o, DTA_SourceType, (&sourcetype),
DTA_Handle, (&fh),
DTA_Name, (&(msid -> msid_ProjectName)),
TAG_DONE ) == 3UL )
{
switch( sourcetype )
{
case DTST_FILE:
{
/* we have the filehandle - anything what we want... */
}
break;
#ifdef HAS_ENCODER
case DTST_RAM:
{
/* do nothing */
}
break;
#endif /* HAS_ENCODER */
default:
{
/* unsupported source type */
error = ERROR_NOT_IMPLEMENTED;
}
break;
}
/* Any error ? */
if( error == 0L )
{
if( fh )
{
/* Run system-stream demultiplexer... */
demux( cb, msid, fh );
/* ...success ? */
if( msid -> Handler . Process )
{
BPTR v_lock;
/* Get first video stream */
if( v_lock = GetStreamLock( cb, msid, 0UL, FALSE ) )
{
if( msid -> msid_CurrVideo = NewDTObject( NULL, DTA_SourceType, DTST_FILE,
DTA_Handle, v_lock,
DTA_GroupID, GID_ANIMATION,
TAG_DONE ) )
{
ULONG video_modeid;
ULONG *video_cregs;
struct ColorRegister *video_cm;
ULONG video_numcolors;
ULONG video_animwidth,
video_animdepth,
video_animheight;
ULONG video_frame,
video_numframes,
video_fps = 0UL,
video_tpf = 0UL;
struct BitMap *video_keyframe;
if( GetDTAttrs( (msid -> msid_CurrVideo),
ADTA_ModeID, (&video_modeid),
ADTA_CRegs, (&video_cregs),
ADTA_ColorRegisters, (&video_cm),
ADTA_NumColors, (&video_numcolors),
ADTA_Width, (&video_animwidth),
ADTA_Height, (&video_animheight),
ADTA_Depth, (&video_animdepth),
ADTA_Frame, (&video_frame),
ADTA_Frames, (&video_numframes),
XTAG( !ISV41, ADTA_FramesPerSecond ), (&video_fps),
XTAG( ISV41, ADTA_TicksPerFrame ), (&video_tpf),
ADTA_KeyFrame, (&video_keyframe),
TAG_DONE ) == 11UL )
{
/* This should NEVER fail, but... */
if( video_numframes && video_keyframe && (video_fps || video_tpf) )
{
ULONG *cregs;
struct ColorRegister *cm;
ULONG numcolors;
SetDTAttrs( o, NULL, NULL, ADTA_NumColors, video_numcolors, TAG_DONE );
if( GetDTAttrs( o, ADTA_CRegs, (&cregs),
ADTA_ColorRegisters, (&cm),
ADTA_NumColors, (&numcolors),
TAG_DONE ) == 3UL )
{
/* Check if we got memory for the palette (if we have one) */
if( (cregs && cm && (numcolors == video_numcolors)) || (video_numcolors == 0UL) )
{
BPTR a_lock;
/* Copy base palette (if we have one) */
if( video_numcolors )
{
CopyMem( video_cregs, cregs, (video_numcolors * 3UL * sizeof( ULONG )) );
CopyMem( video_cm, cm, (video_numcolors * 3UL * sizeof( struct ColorRegister )) );
}
msid -> msid_TicksPerFrame = ((ISV41)?(video_tpf):(TICK_FREQ / video_fps));
SetDTAttrs( o, NULL, NULL, DTA_ObjName, (msid -> msid_ProjectName),
ADTA_ModeID, video_modeid,
ADTA_Width, video_animwidth,
ADTA_Height, video_animheight,
ADTA_Depth, video_animdepth,
ADTA_Frame, video_frame,
ADTA_Frames, video_numframes,
XTAG( !ISV41, ADTA_FramesPerSecond ), video_fps,
XTAG( ISV41, ADTA_TicksPerFrame ), video_tpf,
ADTA_KeyFrame, video_keyframe,
TAG_DONE );
/* Get first audio stream */
if( a_lock = GetStreamLock( cb, msid, 0UL, TRUE ) )
{
if( msid -> msid_CurrAudio = NewDTObject( NULL, DTA_SourceType, DTST_FILE,
DTA_Handle, a_lock,
DTA_GroupID, GID_SOUND,
TAG_DONE ) )
{
ULONG volume;
GetDTAttrs( (msid -> msid_CurrAudio), SDTA_Sample, (&(msid -> msid_Sample)),
SDTA_SampleLength, (&(msid -> msid_SampleLength)),
SDTA_Period, (&(msid -> msid_Period)),
SDTA_Volume, (&volume),
TAG_DONE );
msid -> msid_SamplesPerFrame = ((SysBase -> ex_EClockFrequency) * 10UL) / ((msid -> msid_Period) * (TICK_FREQ / (msid -> msid_TicksPerFrame)) * 2UL);
SetDTAttrs( o, NULL, NULL, ADTA_Sample, (msid -> msid_Sample),
ADTA_SampleLength, (msid -> msid_SamplesPerFrame),
ADTA_Period, (msid -> msid_Period),
ADTA_Volume, volume,
TAG_DONE );
success = TRUE;
}
else
{
/* NewDTObjectA failed */
error = IoErr();
}
}
else
{
/* No audio lock */
error = IoErr();
}
}
else
{
/* no palette memory */
error = ERROR_NO_FREE_STORE;
}
}
else
{
/* We did not get all attributes we need... */
error = ERROR_OBJECT_WRONG_TYPE;
}
}
}
else
{
/* We did not get all attributes we need... */
error = ERROR_OBJECT_WRONG_TYPE;
}
}
else
{
/* NewDTObject failed */
error = IoErr();
}
}
else
{
/* no video stream lock */
error = IoErr();
}
}
else
{
/* demuxer failed */
error = msid -> Demux . retval2;
}
}
else
{
/* No file handle ? - Be sure we got a DTST_RAM sourcetype */
if( sourcetype == DTST_RAM )
{
success = TRUE;
}
else
{
/* No handle ! */
error = ERROR_REQUIRED_ARG_MISSING;
}
}
}
}
else
{
/* can't get required attributes from superclass */
error = ERROR_OBJECT_WRONG_TYPE;
}
}
else
{
/* no memory pool */
error = ERROR_NO_FREE_STORE;
}
SetIoErr( error );
return( success );
}
/*****************************************************************************/
void OpenLogfile( struct ClassBase *cb, struct MPEGSystemInstData *msid )
{
if( ((msid -> msid_VerboseOutput) == NULL) || ((msid -> msid_VerboseOutput) == -1L) )
{
STRPTR confile;
if( confile = (STRPTR)AllocVec( (((msid -> msid_ProjectName)?(strlen( (msid -> msid_ProjectName) )):(0UL)) + 100UL), MEMF_PUBLIC ) )
{
mysprintf( cb, confile, "CON:////MPEG System DataType %s/auto/wait/close/inactive",
((msid -> msid_ProjectName)?(FilePart( (msid -> msid_ProjectName) )):(NULL)) );
msid -> msid_VerboseOutput = Open( confile, MODE_READWRITE );
FreeVec( confile );
}
}
}
static
void mysprintf( struct ClassBase *cb, STRPTR buffer, STRPTR fmt, ... )
{
APTR args;
args = (APTR)((&fmt) + 1);
RawDoFmt( fmt, args, (void (*))"\x16\xc0\x4e\x75", buffer );
}
void error_printf( struct ClassBase *cb, struct MPEGSystemInstData *msid, STRPTR format, ... )
{
OpenLogfile( cb, msid );
if( (msid -> msid_VerboseOutput) && ((msid -> msid_VerboseOutput) != -1L) )
{
VFPrintf( (msid -> msid_VerboseOutput), format, (APTR)((&format) + 1) );
}
}
void syntax_printf( struct ClassBase *cb, struct MPEGSystemInstData *msid, STRPTR format, ... )
{
if( msid -> msid_DoSyntax )
{
OpenLogfile( cb, msid );
if( (msid -> msid_VerboseOutput) && ((msid -> msid_VerboseOutput) != -1L) )
{
VFPrintf( (msid -> msid_VerboseOutput), format, (APTR)((&format) + 1) );
}
}
}
void debug_printf( struct ClassBase *cb, struct MPEGSystemInstData *msid, STRPTR format, ... )
{
if( msid -> msid_DoDebug )
{
OpenLogfile( cb, msid );
if( (msid -> msid_VerboseOutput) && ((msid -> msid_VerboseOutput) != -1L) )
{
VFPrintf( (msid -> msid_VerboseOutput), format, (APTR)((&format) + 1) );
}
}
}
void verbose_printf( struct ClassBase *cb, struct MPEGSystemInstData *msid, STRPTR format, ... )
{
if( (msid -> msid_VerboseOutput) && ((msid -> msid_VerboseOutput) != -1L) )
{
VFPrintf( (msid -> msid_VerboseOutput), format, (APTR)((&format) + 1) );
}
}
static
ULONG SaveMPEGSystem( struct ClassBase *cb, struct IClass *cl, Object *o, struct dtWrite *dtw )
{
ULONG retval = 0UL;
LONG error /*= 0L*/;
#ifdef HAS_ENCODER
/* A NULL file handle is a nop (GMultiView uses this to test if a datatype supports RAW writing) */
if( dtw -> dtw_FileHandle )
{
struct MPEGSystemInstData *msid = (struct MPEGSystemInstData *)INST_DATA( cl, o );
ULONG modeid;
ULONG *cregs;
ULONG numcolors;
ULONG startframe = 0UL,
numframes = 0UL,
framestep = 1UL;
ULONG fps = 0UL;
struct BitMap *keyframe;
ULONG animwidth,
animheight,
animdepth;
if( GetDTAttrs( o, ADTA_ModeID, (&modeid),
ADTA_CRegs, (&cregs),
ADTA_NumColors, (&numcolors),
ADTA_Width, (&animwidth),
ADTA_Height, (&animheight),
ADTA_Depth, (&animdepth),
ADTA_Frame, (&startframe),
ADTA_Frames, (&numframes),
ADTA_FramesPerSecond, (&fps),
ADTA_KeyFrame, (&keyframe),
TAG_DONE ) == 10UL )
{
struct TagItem *tstate,
*ti;
numframes -= startframe;
tstate = dtw -> dtw_AttrList;
while( ti = NextTagItem( (&tstate) ) )
{
switch( ti -> ti_Tag )
{
case ADTA_Frame: startframe = ti -> ti_Data; break;
case ADTA_Frames: numframes = ti -> ti_Data; break;
case ADTA_FrameIncrement: framestep = ti -> ti_Data; break;
}
}
if( framestep == 0UL ) framestep = 1UL;
verbose_printf( cb, msid, "saving mpeg system movie %lu %lu %lu\n", startframe, numframes, framestep );
/* here should follow the encoder part... */
}
else
{
error_printf( cb, msid, "not enougth attributes\n" );
}
}
#else
error = ERROR_NOT_IMPLEMENTED; /* no encoder yet */
#endif /* HAS_ENCODER */
SetIoErr( error );
return( retval );
}
/*****************************************************************************/
static
UBYTE *get_buf_data( struct ClassBase *cb, struct MPEGSystemInstData *msid, long read_len )
{
long len_left;
long len_read;
long temp1;
UBYTE *buf_ptr;
/* Calculate how much data is left in the buffer */
len_left = (msid -> Demux . raw_data_buf_len) - ((msid -> Demux . raw_data_buf_ptr) - (msid -> Demux . raw_data_buf));
/* Not enough for the calling function's needs */
if( len_left < read_len )
{
/* There is some data left, so copy to start of buffer */
if( len_left > 0 )
{
memcpy( (msid -> Demux . raw_data_buf), (msid -> Demux . raw_data_buf_ptr), (size_t)len_left );
}
msid -> Demux . raw_data_buf_ptr = &msid -> Demux . raw_data_buf[ 0 ];
temp1 = MAX_DATA_BUF_LEN - len_left; /* To fill the rest of the buffer */
/* block pos */
msid -> Demux . curr_file_pos = msid -> Demux . fdin_pos; /* Get file pos */
msid -> Demux . curr_file_pos_in_buffer = ((msid -> Demux . raw_data_buf_ptr) + len_left);
debug_printf( cb, msid, "read: %ld, len %ld, left %ld\n", (LONG)(msid -> Demux . curr_file_pos), (LONG)temp1, (LONG)len_left );
len_read = Read( (msid -> Demux . fdin), (msid -> Demux . curr_file_pos_in_buffer), (LONG)temp1 );
if( len_read == -1L )
{
LONG err = IoErr();
error_printf( cb, msid, "Error: reading system file\n" );
exitdemux( cb, msid, RETURN_FAIL, err );
}
else
{
msid -> Demux . fdin_pos += len_read;
}
/* no data left in file - data should never have been requested */
if( len_read == 0 )
{
error_printf( cb, msid, "Error: reached EOF, requested %ld bytes\n", temp1 );
exitdemux( cb, msid, RETURN_FAIL, DTERROR_NOT_ENOUGH_DATA );
}
msid -> Demux . raw_data_buf_len = len_left + len_read;
/* Read done to end of file but still not enough data to complete request */
if( (msid -> Demux . raw_data_buf_len) < read_len )
{
error_printf( cb, msid, "Error: insufficient data for read\n" );
exitdemux( cb, msid, RETURN_FAIL, DTERROR_NOT_ENOUGH_DATA );
}
}
buf_ptr = msid -> Demux . raw_data_buf_ptr; /* For return to calling function. */
msid -> Demux . raw_data_buf_ptr += read_len; /* Global advanced for use at next call. */
return( buf_ptr );
}
static
UBYTE *calc_time_stamp( struct ClassBase *cb, struct MPEGSystemInstData *msid, UBYTE *buf_ptr, long *time_stamp )
{
/* Determine Time Stamp
* Contents:
* Byte 0: 4 bits: depend on SCR/PTS/DTS
* 3 bits: Time32..30,
* 1 bit marker:1
* Byte 1: 8 bits Time29..22.
* Byte 2: 7 bits Time21..15,
* 1 bit marker
* Byte 3: 8 bits Time14..7.
* Byte 4: 7 bits Time6..0,
* 1 bit marker
*/
*time_stamp = ((((long)(*buf_ptr)) & 0x0E) << 29);
/* Marker_bit check */
if( (*buf_ptr & (UBYTE)0x01) != (UBYTE)0x01 )
{
syntax_printf( cb, msid, "Expecting marker_bit value '1' at calc_time_stamp A.\n" );
exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
}
buf_ptr++;
*time_stamp |= ((((long)(*buf_ptr)) & 0xFF) << 22);
buf_ptr++;
*time_stamp |= ((((long)(*buf_ptr)) & 0xFE) << 14);
/* Marker_bit check */
if( (*buf_ptr & (UBYTE)0x01) != (UBYTE)0x01 )
{
syntax_printf( cb, msid, "Expecting marker_bit value '1' at calc_time_stamp B.\n" );
exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
}
buf_ptr++;
*time_stamp |= ((((long)(*buf_ptr)) & 0xFF) << 7);
buf_ptr++;
*time_stamp |= ((((long)(*buf_ptr)) & 0xFE) >> 1);
/* Marker_bit check */
if( (*buf_ptr & (UBYTE)0x01) != (UBYTE)0x01 )
{
syntax_printf( cb, msid, "Expecting marker_bit value '1' at calc_time_stamp C.\n" );
exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
}
buf_ptr++;
return( buf_ptr );
}
static
UBYTE get_next_start_code( struct ClassBase *cb, struct MPEGSystemInstData *msid )
{
UBYTE *buf_ptr = NULL; /* dead assignment, but avoids SAS/C "Warning 317: possibly uninitialized variable" */
UWORD zeros;
/* Scan for start code; should be hex 00 00 01 for Pack Start Code,
* System Start Code, Packet Start Code or ISO_11172_end_code.
* Leading zeros are ignored.
*/
for( ;; )
{
zeros = 0;
for( ;; )
{
buf_ptr = get_buf_data( cb, msid, 1 );
if( *buf_ptr == 0x00 )
{
zeros++;
}
else
{
if( zeros >= 2 )
{
if( *buf_ptr != 0x01 )
{
zeros = 0;
}
else
{
break;
}
}
}
}
buf_ptr = get_buf_data( cb, msid, 1 );
if( (msid -> Demux . system_found) || (*buf_ptr == 0xBB) )
{
break;
}
}
switch( *buf_ptr )
{
case (UBYTE)0xB9:
return( (UBYTE)ISO_11172_END_CODE );
case (UBYTE)0xBA:
return( (UBYTE)PACK_START_CODE );
case (UBYTE)0xBB:
{
msid -> Demux . system_found = TRUE;
verbose_printf( cb, msid, "system start\n" );
return( (UBYTE)SYSTEM_START_CODE );
}
}
return( *buf_ptr ); /* It must be a stream_id. */
}
static
void read_pack_header( struct ClassBase *cb, struct MPEGSystemInstData *msid, PACK_header *pki )
{
UBYTE *buf_ptr;
pki -> SCR = 0;
buf_ptr = get_buf_data( cb, msid, PACK_HEADER_LENGTH );
/* Determine System Clock Reference (SCR)
* Check marker_bits: '0010' = 0x2
*/
if( (*buf_ptr & (UBYTE)0xF0) != (UBYTE)0x20 )
{
syntax_printf( cb, msid, "Expecting '0010' after pack_start_code.\n" );
exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
}
buf_ptr = calc_time_stamp( cb, msid, buf_ptr, (&(pki -> SCR)) );
/* Determine Mux Rate
* Contents:
* Byte 5: 1 bit marker,
* 7 bits mux_rate.
* Byte 6: 8 bits mux_rate.
* Byte 7: 7 bits mux_rate,
* 1 bit marker
*/
pki -> mux_rate = ((((long)(*buf_ptr)) & 0x7F) << 14);
/* Marker_bit check */
if( (*buf_ptr & (UBYTE)0x80) != (UBYTE)0x80 )
{
syntax_printf( cb, msid, "byte= %lx.\n", *buf_ptr );
syntax_printf( cb, msid, "Expecting marker_bit value '1' at read_pack_header A.\n" );
exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
}
buf_ptr++;
pki -> mux_rate |= ((((long)(*buf_ptr)) & 0xFF) << 7);
buf_ptr++;
pki -> mux_rate |= ((((long)(*buf_ptr)) & 0xFE) >> 1);
/* Marker_bit check */
if( (*buf_ptr & (UBYTE)0x01) != (UBYTE)0x01 )
{
syntax_printf( cb, msid, "Expecting marker_bit value '1' at read_pack_header B.\n" );
exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
}
}
static
void read_system_header( struct ClassBase *cb, struct MPEGSystemInstData *msid, SYSTEM_header *si )
{
UBYTE *buf_ptr;
long j;
UBYTE test;
/* Read System Header Length: 2 Bytes after start code.
* Length of header after header length bytes.
* Bytes 0 & 1: 8 bits header_length--high & low
*/
buf_ptr = get_buf_data( cb, msid, 2 );
si -> header_length = (ULONG)(((long)(*buf_ptr)) << 8);
buf_ptr++;
si -> header_length |= (ULONG)(((long)(*buf_ptr)) & 0xFF);
/* Read the rest of the header */
buf_ptr = get_buf_data( cb, msid, (si -> header_length) );
/* Byte 0: 1 bit marker,
* 7 bits rate_bound--high
*/
si -> rate_bound = (ULONG)((((long)*buf_ptr) & 0x7F) << 15);
/* Marker bit check */
if( (*buf_ptr & (UBYTE)0x80) != (UBYTE)0x80 )
{
syntax_printf( cb, msid, "Expecting marker_bit value '1' before rate_bound--high.\n" );
exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
}
buf_ptr++;
/* Byte 1: 8 bits rate_bound--mid */
si -> rate_bound += (ULONG)((((long)*buf_ptr) & 0xFF) << 7);
buf_ptr++;
/* Byte 2: 7 bits rate_bound-low,
* 1 bit marker
*/
si -> rate_bound += (ULONG)((((long)*buf_ptr) & 0xFE) >> 1);
/* Marker bit check */
if( (*buf_ptr & (UBYTE)0x01) != (UBYTE)0x01 )
{
syntax_printf( cb, msid, "Expecting marker_bit value '1' after rate_bound.\n" );
exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
}
buf_ptr++;
/* Byte 3: 6 bits audio_bound,
* 1 bit fixed_flag,
* 1bit CSPS_flag
*/
si -> audio_bound = (ULONG)((((long)*buf_ptr) & 0xFF) >> 2);
si -> fixed_flag = (BOOL)((((long)*buf_ptr) & 0x02) >> 1);
si -> CSPS_flag = (BOOL) (((long)*buf_ptr) & 0x01);
buf_ptr++;
/* Byte 4: 1b system_audio_lock_flag,
* 1b system_video_lock_flag,
* 1b marker,
* 5b video_bound
*/
si -> system_audio_lock_flag = (BOOL)((((long)*buf_ptr) & 0x80) >> 7);
si -> system_video_lock_flag = (BOOL)((((long)*buf_ptr) & 0x40) >> 6);
si -> video_bound = (ULONG) (((long)*buf_ptr) & 0x1F);
/* Marker bit check */
if( (*buf_ptr & (UBYTE)0x20) != (UBYTE)0x20 )
{
syntax_printf( cb, msid, "Expecting marker_bit value '1' after system_video_lock_flag.\n" );
exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
}
buf_ptr++;
if( *buf_ptr != (UBYTE)0xFF )
{
syntax_printf( cb, msid, "Reserved byte not set to default\n" );
exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
}
buf_ptr++;
if( (si -> header_length) == 6 )
{
return;
}
j = 0;
while( (ULONG)buf_ptr < (ULONG)(msid -> Demux . raw_data_buf_ptr) )
{
/* All lmntry stream_id's ... */
test = (*buf_ptr & (UBYTE)0x80);
if( test == 0 )
{
error_printf( cb, msid, "system header length incorrect\n" );
exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
}
si -> STD_buffer_info[ j ] . stream_id = (*buf_ptr & (UBYTE)0xFF);
buf_ptr++;
/* Check marker_bits */
if( (*buf_ptr & (UBYTE)0xC0) != (UBYTE)0xC0 )
{
syntax_printf( cb, msid, "Expecting placeholder bit values '11' after stream_id.\n" );
exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
}
si -> STD_buffer_info[ j ] . STD_buffer_bound_scale = (*buf_ptr & (UBYTE)0x20) >> 5;
si -> STD_buffer_info[ j ] . STD_buffer_size_bound = (*buf_ptr & (UBYTE)0x1F) << 8;
buf_ptr++;
si -> STD_buffer_info[ j ] . STD_buffer_size_bound |= *buf_ptr & (UBYTE)0xFF;
buf_ptr++;
j++;
}
}
static
void read_packet_header( struct ClassBase *cb, struct MPEGSystemInstData *msid, PACKET_header *pi )
{
UBYTE *buf_ptr;
UBYTE test,
test1;
long i;
pi -> PTS = 0;
pi -> DTS = 0;
/* read packet length */
buf_ptr = get_buf_data( cb, msid, 2 );
/* Bytes 0 & 1: 8 bits packet_length -- high & low */
pi -> packetlength = (ULONG)(((long)*buf_ptr) << 8);
buf_ptr++;
pi -> packetlength |= (ULONG)(((long)*buf_ptr) & 0xFF);
/* Get the remainder of the packet. */
buf_ptr = get_buf_data( cb, msid, (pi -> packetlength) );
/* private stream 2 */
if( (pi -> stream_id) <= (UBYTE)0xBF )
{
pi -> packet_ptr = buf_ptr;
return;
}
/* Skip over any stuffing Bytes 1111 1111 (16 max)
* Count to 17 to see if there are more than allowed in the spec.
*/
i = 0;
while( (*buf_ptr == (UBYTE)0xFF) && (i < 17) )
{
buf_ptr++;
pi -> packetlength--;
i++;
}
if( i == 17 )
{
error_printf( cb, msid, "Too many stuffing bytes after packet_length\n" );
exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
}
/* If nextbits =='01' read STD_buffer_scale & _size
* Byte: 2b '01',
* 1b STD_buffer_scale,
* 5b STD_buffer_size-hi
* Byte: 8 bits STD_buffer_size--lo
*/
test1 = *buf_ptr & (UBYTE)0xC0;
if( test1 == (UBYTE)0x40 )
{
/* read STD values */
pi -> STD_buffer_scale = (*buf_ptr & (UBYTE)0x20) >> 5;
pi -> STD_buffer_size = (*buf_ptr & (UBYTE)0x1F) << 8;
buf_ptr++;
pi -> packetlength--;
pi -> STD_buffer_size |= (*buf_ptr & (UBYTE)0xFF);
buf_ptr++;
pi -> packetlength--;
}
/* Determine whether to read in PTS, PTS & DTS, or neither before data. */
test = (*buf_ptr & (UBYTE)0xF0);
switch( test )
{
case (UBYTE)0x20: /* Get PTS only */
{
buf_ptr = calc_time_stamp( cb, msid, buf_ptr, (&(pi -> PTS)) );
pi -> packetlength -= 5;
}
break;
case (UBYTE)0x30: /* Get PTS & DTS. Both same format as PTS above.
* pts and data
*/
{
buf_ptr = calc_time_stamp( cb, msid, buf_ptr, (&(pi -> PTS)) );
/* Get DTS */
if( (*buf_ptr & (UBYTE)0xF0) != (UBYTE)0x10 )
{
syntax_printf( cb, msid, "Expecting bits '0001' before DTS in read_packet_header\n" );
exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
}
buf_ptr = calc_time_stamp( cb, msid, buf_ptr, (&(pi -> DTS)) );
pi -> packetlength -= 10;
}
break;
case (UBYTE)0x00:
{
if( *buf_ptr != (UBYTE)0x0F )
{
error_printf( cb, msid, "not a valid packet time sequence\n" );
exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
}
buf_ptr++;
pi -> packetlength--;
}
break;
default:
{
error_printf( cb, msid, "invalid time code in packet\n" );
exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
}
break;
}
/* packetlength should have been properly set by the above */
pi -> packet_ptr = buf_ptr;
}
static
void print_pack_header( struct ClassBase *cb, struct MPEGSystemInstData *msid, PACK_header *pki )
{
long mux_byte_rate = (pki -> mux_rate) * 50;
verbose_printf( cb, msid, "PACK HEADER: SCR = %ld, Mux Rate = %ld, or %ld Bytes per second\n",
(LONG)(pki -> SCR), (LONG)(pki -> mux_rate), mux_byte_rate );
}
static
void print_system_header( struct ClassBase *cb, struct MPEGSystemInstData *msid, SYSTEM_header *si )
{
long i = 0;
long stream_num = 0;
long buf_byte_size_bound;
UBYTE stream_type = 0;
verbose_printf( cb, msid, "SYSTEM HEADER: Hdr Len = %ld, Rate bound = %ld, Audio bound = %ld,\n",
(LONG)(si -> header_length),
(LONG)(si -> rate_bound),
(LONG)(si -> audio_bound) );
verbose_printf( cb, msid, "Fixed flag = %ld, CSPS flag = %ld, Sys audio lock flag = %ld,\n",
(LONG)(si -> fixed_flag),
(LONG)(si -> CSPS_flag),
(LONG)(si -> system_audio_lock_flag) );
verbose_printf( cb, msid, "Sys video lock flag = %ld, Video bound = %ld\n",
(LONG)(si -> system_video_lock_flag), (LONG)(si -> video_bound) );
while( si -> STD_buffer_info[ i ] . stream_id != 0 )
{
if( ((si -> STD_buffer_info[ i ] . stream_id >> 5) & 0x6) == 0x6 )
{
stream_type = 'A';
stream_num = (long)((si -> STD_buffer_info[ i ] . stream_id) & 0x1F);
}
if( ((si -> STD_buffer_info[ i ] . stream_id >> 4) & 0xE) == 0xE )
{
stream_type = 'V';
stream_num = (long)((si -> STD_buffer_info[ i] . stream_id) & 0x0F);
}
if( si -> STD_buffer_info[ i ] . STD_buffer_bound_scale == 0 )
{
buf_byte_size_bound = si -> STD_buffer_info[ i ] . STD_buffer_size_bound * 128;
}
else
{
buf_byte_size_bound= si -> STD_buffer_info[ i ] . STD_buffer_size_bound * 1024;
}
verbose_printf( cb, msid, "STD buf %ld: Stream %lc%ld, Buf bound scale %ld, Buf size bound %ld or %ld bytes\n",
i, stream_type, stream_num,
(si -> STD_buffer_info[ i ] . STD_buffer_bound_scale),
(si -> STD_buffer_info[ i ] . STD_buffer_size_bound),
buf_byte_size_bound );
i++;
}
}
static
void print_packet_header( struct ClassBase *cb, struct MPEGSystemInstData *msid, PACKET_header *pi )
{
long stream_num = 0;
UBYTE stream_type = 0;
if( ((pi -> stream_id >> 5) & 0x6) == 0x6 )
{
stream_type = 'A';
stream_num = (long)((pi -> stream_id) & 0x1F);
}
if( ((pi -> stream_id >> 4) & 0xE) == 0xE )
{
stream_type = 'V';
stream_num = (long)((pi -> stream_id) & 0x0F);
}
verbose_printf( cb, msid, "PACKET HDR %lc%ld: %ldB, STD buf scale %ld, ..size %ld, PTS %ld, DTS %ld\n",
stream_type,
stream_num,
(pi -> packetlength),
(pi -> STD_buffer_scale),
(pi -> STD_buffer_size),
(pi -> PTS),
(pi -> DTS) );
}
static
void demux( struct ClassBase *cb, struct MPEGSystemInstData *msid, BPTR file )
{
PACK_header pack_info;
PACK_header *pack_info_p = &pack_info;
SYSTEM_header sys_info;
SYSTEM_header *sys_info_p = &sys_info;
PACKET_header pkt_info;
PACKET_header *pkt_info_p = &pkt_info;
long i,
j;
long current_audio = 0;
long current_video = 0;
long demux_id[ MAX_MPEG_STREAMS ];
UBYTE next_code;
/* Alloc input buffer */
if( msid -> Demux . raw_data_buf = (UBYTE *)AllocVec( MAX_DATA_BUF_LEN, MEMF_PUBLIC ) )
{
/* Set "point of return" on decoder exit/error */
if( setjmp( (msid -> Demux . exit_buf) ) == 0 )
{
/* Init remaining fields */
msid -> Demux . raw_data_buf_len = 0;
msid -> Demux . raw_data_buf_ptr = msid -> Demux . raw_data_buf;
msid -> Demux . fdin = file;
msid -> Demux . fdin_pos = 0L;
/* Read and write data. */
for( ;; )
{
next_code = get_next_start_code( cb, msid );
verbose_printf( cb, msid, "next_code = %lx\n", (LONG)next_code );
/* next_code identifies whether the stream is at
* the beginning of a system stream, a pack, or a packet,
* or if it is at the end of a system stream.
*/
switch( next_code )
{
case ((UBYTE)PACK_START_CODE): /* 0xBA */
{
read_pack_header( cb, msid, pack_info_p );
print_pack_header( cb, msid, pack_info_p );
}
break;
case ((UBYTE)SYSTEM_START_CODE): /* 0xBB */
{
read_system_header( cb, msid, sys_info_p );
print_system_header( cb, msid, sys_info_p );
msid -> Demux . N = sys_info_p -> audio_bound;
msid -> Demux . M = sys_info_p -> video_bound;
verbose_printf( cb, msid, " %ld audio, %ld video\n", (msid -> Demux . N), (msid -> Demux . M) );
current_audio = 0;
current_video = msid -> Demux . N;
for( i = 0 ; i < MAX_MPEG_STREAMS ; i++ )
{
demux_id[ i ] = -1;
}
}
break;
case ((UBYTE)ISO_11172_END_CODE): /* 0xB9 */
{
verbose_printf( cb, msid, "end of system stream reached\n" );
exitdemux( cb, msid, RETURN_OK, 0L );
}
break;
default: /* PACKET START CODE & stream_id */
{
pkt_info_p -> stream_id = next_code;
read_packet_header( cb, msid, pkt_info_p );
print_packet_header( cb, msid, pkt_info_p );
/* The stream_id identifies the stream by type and number */
switch( pkt_info_p -> stream_id )
{
case( (UBYTE)0xB8 ): /* may need more work */
{
/* Following STD_buffer_scale and _size refer to
* all audio streams in the muxed stream
*/
error_printf( cb, msid, "Error handling common audio STD_buffer_scale and _size.\n" );
exitdemux( cb, msid, RETURN_FAIL, DTERROR_UNKNOWN_COMPRESSION );
}
break;
case( (UBYTE)0xB9 ): /* may need more work */
{
/* Following STD_buffer_scale and _size refer to
* all video streams in the muxed stream
*/
error_printf( cb, msid, "Error handling common video STD_buffer_scale and _size.\n" );
exitdemux( cb, msid, RETURN_FAIL, DTERROR_UNKNOWN_COMPRESSION );
}
break;
case( (UBYTE)0xBC ):
{
/* Reserved stream: discard */
error_printf( cb, msid, "Received reserved stream\n" );
exitdemux( cb, msid, RETURN_FAIL, DTERROR_UNKNOWN_COMPRESSION );
}
break;
case( (UBYTE)0xBD ):
{
/* private_stream_1: discard */
error_printf( cb, msid, "Received private_stream_1\n" );
exitdemux( cb, msid, RETURN_FAIL, DTERROR_UNKNOWN_COMPRESSION );
}
break;
case( (UBYTE)0xBE ): /* data bytes all FF */
{
/* Padding stream: discard */
verbose_printf( cb, msid, "Received padding stream\n" );
}
break;
case( (UBYTE)0xBF ):
{
/* private_stream_2: discard */
error_printf( cb, msid, "Received private_stream_2\n" );
exitdemux( cb, msid, RETURN_FAIL, DTERROR_UNKNOWN_COMPRESSION );
}
break;
default:
{
/* The stream is an audio, video, or reserved_data stream.
* Convert stream_id to C0 to 0, EF to 48
*/
i = (((long)pkt_info_p -> stream_id) & 0xFF) - 192;
if( i < 0 )
{
/* Invalid stream: do nothing */
}
else
{
/* Audio or Video stream. */
if( i < 48 )
{
BOOL isaudio = (i < 32);
struct PacketBlock *pb;
if( demux_id[ i ] == -1 )
{
/* A new stream */
if( isaudio )
{
/* Audio */
demux_id[ i ] = current_audio;
current_audio++;
}
else
{
demux_id[ i ] = current_video;
current_video++;
}
}
j = demux_id[ i ];
/* No file ? - Then create it ! */
if( (msid -> Demux . fd_demuxa[ j ] . sl_handle) == NULL )
{
NewList( (struct List *)(&(msid -> Demux . fd_demuxa[ j ] . sl_packetblocklist)) );
msid -> Demux . fd_demuxa[ j ] . sl_handle = msid -> Demux . fdin;
}
/* block pos */
verbose_printf( cb, msid, "write: off %ld len %ld\n", (msid -> Demux . curr_file_pos + /* file pos */
(long)((pkt_info_p -> packet_ptr) - msid -> Demux . curr_file_pos_in_buffer)),
(pkt_info_p -> packetlength) );
/* Create, fill and queue a PacketBlock node */
pb = (struct PacketBlock *)AllocPooled( (msid -> msid_Pool), (ULONG)sizeof( struct PacketBlock ) );
if( pb == NULL ) exitdemux( cb, msid, RETURN_FAIL, ERROR_NO_FREE_STORE );
pb -> system_offset = (msid -> Demux . curr_file_pos + (long)((pkt_info_p -> packet_ptr) - msid -> Demux . curr_file_pos_in_buffer)); /* file pos */
pb -> length = (pkt_info_p -> packetlength);
/* Inc stream size */
msid -> Demux . fd_demuxa[ j ] . sl_size += pb -> length;
AddTail( (struct List *)(&(msid -> Demux . fd_demuxa[ j ] . sl_packetblocklist)), (struct Node *)(&(pb -> node)) );
}
else
{
if( i >= 48 )
{
/* It's a reserved data stream.
* Additional functionality will be implementation dependent
*/
verbose_printf( cb, msid, "Reserved data stream %ld is being ignored.\n", i );
break;
}
else
{
/* Should never be reached. */
error_printf( cb, msid, "Unrecognized stream ID: %ld %lx %lc\n",
(pkt_info_p -> stream_id),
(pkt_info_p -> stream_id),
(long)(pkt_info_p -> stream_id) );
exitdemux( cb, msid, RETURN_FAIL, DTERROR_UNKNOWN_COMPRESSION );
}
}
}
}
break;
}
}
}
}
}
/* Success ? */
if( ((msid -> Demux . retval) <= RETURN_WARN) || (msid -> msid_IgnoreErrors) )
{
RunHandler( cb, msid );
}
FreeVec( (msid -> Demux . raw_data_buf) );
}
}
static
void exitdemux( struct ClassBase *cb, struct MPEGSystemInstData *msid, LONG result, LONG result2 )
{
msid -> Demux . retval = result;
msid -> Demux . retval2 = result2;
longjmp( (msid -> Demux . exit_buf), 1 );
}
/*****************************************************************************/
static
BPTR GetStreamLock( struct ClassBase *cb, struct MPEGSystemInstData *msid, ULONG num, BOOL isaudio )
{
if( msid -> Handler . Process )
{
BPTR lock;
num += (isaudio)?(0UL):(msid -> Demux . N); /* Add video ID-offset if neccesary */
lock = (BPTR)DoPkt( (&(msid -> Handler . Process -> pr_MsgPort)), ACTION_COPY_DIR, MKBADDR( (num * 16) ), 0L, 0L, 0L, 0L );
return( lock );
}
return( NULL );
}
static
void RunHandler( struct ClassBase *cb, struct MPEGSystemInstData *msid )
{
if( msid -> Handler . Process = CreateNewProcTags( NP_Entry, (ULONG)Handler,
NP_Name, (ULONG)"mpegsystem.datatype (MPEG System Demux Handler)",
NP_Priority, 15L,
NP_StackSize, 16384UL,
TAG_DONE ) )
{
/* Initialize the message */
msid -> Handler . Startup . sm_Message . mn_Node . ln_Type = NT_MESSAGE;
msid -> Handler . Startup . sm_Message . mn_Length = sizeof( struct StartupMsg );
msid -> Handler . Startup . sm_CB = cb;
msid -> Handler . Startup . sm_msid = msid;
/* Send the information to the process */
PutMsg( (&(msid -> Handler . Process -> pr_MsgPort)), (&(msid -> Handler . Startup . sm_Message)) );
}
}
/* Kill our virtual demux filesystem and wait until it is really dead...
* (Should be replaced by a version which uses exec.library/Wait)
*/
static
void KillHandler( struct ClassBase *cb, struct MPEGSystemInstData *msid )
{
while( (msid -> Handler . Process) )
{
Forbid();
if( msid -> Handler . Process )
{
Signal( (&(msid -> Handler . Process -> pr_Task)), SIGBREAKF_CTRL_C );
}
Permit();
/* Wait a 1/25 sec before trying it again... */
Delay( (TICKS_PER_SECOND / 25UL) );
}
}
/* virtual demux filesystem handler entry */
DISPATCHERFLAGS
static
void Handler( void )
{
#ifdef SysBase
#undef SysBase
#endif
struct ExecBase *SysBase = (*((struct ExecBase **)4UL));
struct Process *pr;
struct StartupMsg *sm;
struct ClassBase *cb;
struct MPEGSystemInstData *msid;
ULONG sigflags;
struct Message *msg;
BOOL done = FALSE;
/* Find out who we are */
pr = (struct Process *)FindTask( NULL );
/* Get the startup message */
WaitPort( (&(pr -> pr_MsgPort)) );
sm = (struct StartupMsg *)GetMsg( (&(pr -> pr_MsgPort)) );
/* Pull all the information required from the startup message */
cb = sm -> sm_CB;
msid = sm -> sm_msid;
/* BUG: The parent process does currently not handle a failure if this process... */
while( (msid -> Handler . mp = CreateMsgPort()) == NULL )
{
}
/* Init DOS_STDPKT queue which is used within the handler to avoid a AllocDOSObject
* and fill it up with some packets...
*/
{
ULONG i;
NewList( (struct List *)(&(msid -> Handler . pktpool)) );
for( i = 0UL ; i < 16UL ; i++ )
{
struct DosPacket *dp;
if( dp = (struct DosPacket *)AllocDosObject( DOS_STDPKT, NULL ) )
{
AddTail( (struct List *)(&(msid -> Handler . pktpool)), (&(dp -> dp_Link -> mn_Node)) );
}
else
{
break;
}
}
}
do
{
sigflags = Wait( (1UL << (pr -> pr_MsgPort . mp_SigBit)) | SIGBREAKF_CTRL_C );
if( sigflags & SIGBREAKF_CTRL_C )
{
done = TRUE;
}
while( msg = GetMsg( (&(pr -> pr_MsgPort)) ) )
{
dispatch_packet( cb, msid, (struct DosPacket *)(msg -> mn_Node . ln_Name) );
}
} while( (!done) || (msid -> Handler . OpenCount) );
/* Free DOS_STDPKT queue */
{
struct Node *node;
while( node = RemHead( (struct List *)(&(msid -> Handler . pktpool)) ) )
{
struct DosPacket *dp = (struct DosPacket *)(node -> ln_Name);
FreeDosObject( DOS_STDPKT, (APTR)dp );
}
}
DeleteMsgPort( (msid -> Handler . mp) );
Forbid();
msid -> Handler . Process = NULL;
#define SysBase (cb -> cb_SysBase)
}
static
void dispatch_packet( struct ClassBase *cb, struct MPEGSystemInstData *msid, struct DosPacket *dp )
{
struct MsgPort *rport;
struct StreamHandle *sh;
switch( dp -> dp_Type )
{
case ACTION_FINDINPUT:
case ACTION_FH_FROM_LOCK:
{
struct FileHandle *fh = (struct FileHandle *)BADDR( (dp -> dp_Arg1) );
struct FileLock *fl = (struct FileLock *)BADDR( (dp -> dp_Arg2) );
LONG stream_index = fl -> fl_Key;
if( sh = (struct StreamHandle *)AllocVec( sizeof( struct StreamHandle ), MEMF_PUBLIC ) )
{
/* init StreamHandle */
sh -> sl = (&(msid -> Demux . fd_demuxa[ stream_index ]));
sh -> currpos = 0L;
/* Init FileHandle */
fh -> fh_Port = (struct MsgPort *)DOSFALSE; /* non-interactive handler */
fh -> fh_Arg1 = (LONG)sh; /* set to struct StreamHandle */
fh -> fh_Type = (&(msid -> Handler . Process -> pr_MsgPort));
/* Handler in-use */
msid -> Handler . OpenCount++;
if( (dp -> dp_Type) == ACTION_FH_FROM_LOCK )
{
/* Here we can "eat" the given filelock... */
FreeLock( cb, msid, fl );
}
dp -> dp_Res1 = DOSTRUE; /* success */
dp -> dp_Res2 = 0L; /* No error */
}
else
{
dp -> dp_Res1 = DOSFALSE; /* failure */
dp -> dp_Res2 = IoErr(); /* Cause ? */
}
}
break;
case ACTION_END:
{
sh = (struct StreamHandle *)(dp -> dp_Arg1);
if( sh )
{
FreeVec( sh );
}
msid -> Handler . OpenCount--;
dp -> dp_Res1 = DOSTRUE;
dp -> dp_Res2 = 0L;
}
break;
case ACTION_READ:
{
LONG stream_offset = 0L;
LONG read_size = (dp -> dp_Arg3);
UBYTE *dest = (UBYTE *)(dp -> dp_Arg2);
LONG error = 0L;
LONG curr_read_size = 0L; /* bytes already in buffer */
LONG numpkt = 0L; /* pending packets */
struct FileHandle *fh;
struct PacketBlock *worknode,
*nextnode;
sh = (struct StreamHandle *)(dp -> dp_Arg1);
/* We use "fdin" instead of "sl_handle" here because our whole bunch of virtual streams are mapped to
* one real system stream. "sl_handle" would only be used if we have one real stream handle for each
* of our virtual handles. The same refers to the actual file position of the real stream handle
* (see "classdata.h", "sl_handle_pos".
*/
#ifdef COMMENTED_OUT
fh = BADDR( (sh -> sl -> sl_handle) );
#else
fh = BADDR( (msid -> Demux . fdin) );
#endif /* COMMENTED_OUT */
worknode = (struct PacketBlock *)(sh -> sl -> sl_packetblocklist . mlh_Head);
while( (nextnode = (struct PacketBlock *)(worknode -> node . mln_Succ)) && (read_size > 0UL) )
{
/* Find start block */
if( (stream_offset <= (sh -> currpos)) &&
((stream_offset + (worknode -> length)) > (sh -> currpos)) )
{
LONG cut = ((sh -> currpos) - stream_offset),
curr_readlen = MIN( read_size, ((worknode -> length) - cut) );
LONG abs_seek_pos; /* seek position, relative from the beginning of the file */
#if 0
if( Seek( (sh -> sl -> sl_handle), ((worknode -> system_offset) + cut), OFFSET_BEGINNING ) == -1L )
{
break;
}
#else
abs_seek_pos = ((worknode -> system_offset) + cut);
if( SendSeek( cb, msid, fh, (abs_seek_pos - (LONG)(msid -> Demux . fdin_pos)), OFFSET_CURRENT ) )
{
numpkt++;
msid -> Demux . fdin_pos = abs_seek_pos;
}
else
{
error = IoErr();
break;
}
#endif
/* We only need to check for a "complete fill" or failure, because the previous scan
* (in the demuxer part) gurantees that the buffer (packet) has at least this ("curr_readlen") size
*/
#if 0
if( Read( (sh -> sl -> sl_handle), dest, curr_readlen ) != curr_readlen )
{
error = IoErr();
break;
}
#else
if( SendRead( cb, msid, fh, dest, curr_readlen ) )
{
numpkt++;
msid -> Demux . fdin_pos += curr_readlen;
}
else
{
error = IoErr();
break;
}
#endif
dest += curr_readlen;
read_size -= curr_readlen;
curr_read_size += curr_readlen;
sh -> currpos += curr_readlen;
/* Should never ever happen, but... */
if( read_size < 0L )
{
error = ERROR_BUFFER_OVERFLOW;
break;
}
}
stream_offset += worknode -> length;
worknode = nextnode;
}
#if 1
/* Wait for outstanding msgs and fetch success/errorc codes from them */
while( numpkt > 0L )
{
struct Message *msg;
WaitPort( (msid -> Handler . mp) );
while( msg = GetMsg( (msid -> Handler . mp) ) )
{
struct DosPacket *rdp = (struct DosPacket *)(msg -> mn_Node . ln_Name);
/* This assumes that we only get ACTION_SEEK/ACTION_READ here back... */
if( (rdp -> dp_Res1) == -1L )
{
error = rdp -> dp_Res2;
}
ReleaseDOSPacket( cb, msid, rdp );
numpkt--;
}
}
#endif
/* Resync on error. Required because we don't know whether the ACTION_READ (see above)
* was executed or not.
*/
if( error )
{
msid -> Demux . fdin_pos = Seek( (msid -> Demux . fdin), 0L, OFFSET_CURRENT );
}
dp -> dp_Res1 = ((error)?(-1L):(curr_read_size)); /* bytes read */
dp -> dp_Res2 = error; /* result2 */
}
break;
case ACTION_SEEK:
{
LONG newpos = 0L;
sh = (struct StreamHandle *)(dp -> dp_Arg1);
switch( dp -> dp_Arg3 )
{
case OFFSET_BEGINNING:
{
newpos = dp -> dp_Arg2;
}
break;
case OFFSET_CURRENT:
{
newpos += (sh -> currpos) + (dp -> dp_Arg2);
}
break;
case OFFSET_END:
{
newpos += (sh -> sl -> sl_size) + (dp -> dp_Arg2);
}
break;
default: /* should return ERROR_SEEK_ERROR */
{
newpos = -1L; /* forces ERROR_SEEK_ERROR by bounds check below */
}
break;
}
/* Check bounds */
if( (newpos > (sh -> sl -> sl_size)) || (newpos < 0L) )
{
dp -> dp_Res1 = -1L;
dp -> dp_Res2 = ERROR_SEEK_ERROR;
}
else
{
dp -> dp_Res1 = sh -> currpos; /* Return abolsute position BEFORE the seek took place */
dp -> dp_Res2 = 0L;
sh -> currpos = newpos;
}
}
break;
case ACTION_COPY_DIR_FH:
{
struct FileLock *dest;
struct StreamList *sl = ((struct StreamHandle *)(dp -> dp_Arg1)) -> sl;
if( dest = (struct FileLock *)AllocVec( sizeof( struct FileLock ), MEMF_PUBLIC ) )
{
LONG stream_index = (sl - (&(msid -> Demux . fd_demuxa[ 0 ]))); /* Return array index of "sl" */
dest -> fl_Link = NULL;
dest -> fl_Key = stream_index;
dest -> fl_Access = ACCESS_READ;
dest -> fl_Task = (&(msid -> Handler . Process -> pr_MsgPort));
dest -> fl_Volume = NULL;
msid -> Handler . OpenCount++;
}
dp -> dp_Res1 = MKBADDR( dest );
dp -> dp_Res2 = (dp -> dp_Res1)?(0L):(IoErr());
}
break;
case ACTION_COPY_DIR:
{
struct FileLock *src = (struct FileLock *)BADDR( (dp -> dp_Arg1) ),
*dest;
if( dest = (struct FileLock *)AllocVec( sizeof( struct FileLock ), MEMF_PUBLIC ) )
{
if( (LONG)src > 256L )
{
*dest = *src;
msid -> Handler . OpenCount++;
}
else
{
LONG key = ((LONG)src / 16); /* src is now the index... */
/* Does we have a system stream to work on ? */
if( msid -> Demux . fd_demuxa[ key ] . sl_handle )
{
dest -> fl_Link = NULL;
dest -> fl_Key = key;
dest -> fl_Access = ACCESS_READ;
dest -> fl_Task = (&(msid -> Handler . Process -> pr_MsgPort));
dest -> fl_Volume = NULL;
msid -> Handler . OpenCount++;
}
else
{
FreeVec( dest );
dest = NULL;
SetIoErr( ERROR_OBJECT_NOT_FOUND );
}
}
}
dp -> dp_Res1 = MKBADDR( dest );
dp -> dp_Res2 = (dp -> dp_Res1)?(0L):(IoErr());
}
break;
case ACTION_FREE_LOCK:
{
struct FileLock *fl = (struct FileLock *)BADDR( (dp -> dp_Arg1) );
FreeLock( cb, msid, fl );
dp -> dp_Res1 = DOSTRUE;
dp -> dp_Res2 = 0L;
}
break;
case ACTION_IS_FILESYSTEM:
{
dp -> dp_Res1 = DOSTRUE; /* We are a filesystem */
dp -> dp_Res2 = 0L; /* Avoid Lock( ":", SHARED_LOCK ); overhead,
* see Ralf Babel's "The Amiga GURU Book",
* section 21.2.3.17, page 621
*/
}
break;
case ACTION_EXAMINE_OBJECT:
{
struct FileLock *fl = (struct FileLock *)BADDR( (dp -> dp_Arg1) );
struct FileInfoBlock *fib = (struct FileInfoBlock *)BADDR( (dp -> dp_Arg2) );
LONG stream_index = fl -> fl_Key;
struct StreamList *sl = (&(msid -> Demux . fd_demuxa[ stream_index ]));
dp -> dp_Res1 = ExamineFH( (sl -> sl_handle), fib );
if( dp -> dp_Res1 )
{
TEXT buffer[ 256 ];
mysprintf( cb, buffer, "%s@%ld", (fib -> fib_FileName), stream_index );
stccpy( (fib -> fib_FileName), buffer, sizeof( (fib -> fib_FileName) ) );
/* Set our stream size */
fib -> fib_Size = sl -> sl_size;
/* Bump number of blocks */
fib -> fib_NumBlocks = INTDIVR( (sl -> sl_size), 1024UL ); /* BUG: Should be packet size to allow faster reading
* in conjunction with async I/O
*/
/* Convert to BCPL strings */
STRPTR2BSTR( (fib -> fib_FileName) );
STRPTR2BSTR( (fib -> fib_Comment) );
dp -> dp_Res2 = 0L;
}
else
{
dp -> dp_Res2 = IoErr();
}
}
break;
case ACTION_PARENT:
{
struct FileLock *fl = (struct FileLock *)BADDR( (dp -> dp_Arg1) );
LONG stream_index = fl -> fl_Key;
struct StreamList *sl = (&(msid -> Demux . fd_demuxa[ stream_index ]));
dp -> dp_Res1 = ParentOfFH( (sl -> sl_handle) ); /* Get parent of system stream */
dp -> dp_Res2 = IoErr();
}
break;
case ACTION_INFO: /* Required for async I/O */
default:
{
dp -> dp_Res1 = DOSFALSE;
dp -> dp_Res2 = ERROR_ACTION_NOT_KNOWN;
}
break;
}
rport = dp -> dp_Port;
dp -> dp_Port = (&(msid -> Handler . Process -> pr_MsgPort));
PutMsg( rport, (dp -> dp_Link) );
}
/* Convert a C string into a BCPL string */
static
void STRPTR2BSTR( STRPTR s )
{
size_t len = strlen( s );
memmove( (&s[ 1 ]), s, len );
s[ 0 ] = len; /* WARNING: Assumes that the string is less than < 255 bytes ! */
}
static
void FreeLock( struct ClassBase *cb, struct MPEGSystemInstData *msid, struct FileLock *fl )
{
if( fl )
{
fl -> fl_Task = NULL; /* Any attempt to use the FileLock again should be DEADLY !! */
msid -> Handler . OpenCount--;
FreeVec( fl );
}
}
/* Fetch a DosPacket from our private pool */
static
struct DosPacket *ObtainDOSPacket( struct ClassBase *cb, struct MPEGSystemInstData *msid )
{
struct DosPacket *dp;
struct Node *node;
if( node = RemHead( (struct List *)(&(msid -> Handler . pktpool)) ) )
{
dp = (struct DosPacket *)(node -> ln_Name);
}
else
{
dp = (struct DosPacket *)AllocDosObject( DOS_STDPKT, NULL );
}
return( dp );
}
/* Return a DosPacket to our private pool */
static
void ReleaseDOSPacket( struct ClassBase *cb, struct MPEGSystemInstData *msid, struct DosPacket *dp )
{
AddTail( (struct List *)(&(msid -> Handler . pktpool)), (&(dp -> dp_Link -> mn_Node)) );
}
/* Send a seek async */
static
BOOL SendSeek( struct ClassBase *cb, struct MPEGSystemInstData *msid, struct FileHandle *fh, LONG pos, LONG mode )
{
struct DosPacket *dp;
if( dp = ObtainDOSPacket( cb, msid ) )
{
dp -> dp_Type = ACTION_SEEK;
dp -> dp_Arg1 = fh -> fh_Arg1;
dp -> dp_Arg2 = pos;
dp -> dp_Arg3 = mode;
dp -> dp_Port = dp -> dp_Link -> mn_ReplyPort = msid -> Handler . mp;
PutMsg( (fh -> fh_Type), (dp -> dp_Link) );
return( TRUE );
}
return( FALSE );
}
/* Send a read async */
static
BOOL SendRead( struct ClassBase *cb, struct MPEGSystemInstData *msid, struct FileHandle *fh, UBYTE *buffer, ULONG len )
{
struct DosPacket *dp;
if( dp = ObtainDOSPacket( cb, msid ) )
{
dp -> dp_Type = ACTION_READ;
dp -> dp_Arg1 = fh -> fh_Arg1;
dp -> dp_Arg2 = (LONG)buffer;
dp -> dp_Arg3 = len;
dp -> dp_Port = dp -> dp_Link -> mn_ReplyPort = msid -> Handler . mp;
PutMsg( (fh -> fh_Type), (dp -> dp_Link) );
return( TRUE );
}
return( FALSE );
}